#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <openssl/evp.h>  // OpenSSL 头文件
#include <dirent.h>
extern char **environ;  // 声明全局环境变量

#define ATTACH_MAGIC "ATTACHEND"
#define ATTACH_MAGIC_LEN 9

// 附加数据信息结构
typedef struct {
    unsigned char *data;
    size_t len;
} attached_data_t;

// 从可执行文件末尾读取附加数据
attached_data_t* read_attached_data() {
    // 获取当前可执行文件路径
    char exe_path[PATH_MAX];
    ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
    if (len == -1) {
        perror("readlink");
        return NULL;
    }
    exe_path[len] = '\0';
    
    // 打开可执行文件
    int fd = open(exe_path, O_RDONLY);
    if (fd < 0) {
        perror("open");
        return NULL;
    }
    
    // 获取文件大小
    off_t file_size = lseek(fd, 0, SEEK_END);
    if (file_size < ATTACH_MAGIC_LEN + sizeof(size_t)) {
        fprintf(stderr, "文件太小，没有附加数据\n");
        close(fd);
        return NULL;
    }
    
    // 调试信息
    fprintf(stderr, "文件大小: %ld\n", file_size);
    
    // 读取魔数和数据长度
    char magic[ATTACH_MAGIC_LEN + 1] = {0};
    size_t data_len = 0;
    
    // 读取魔数 (从文件末尾倒数第10个字节开始)
    off_t magic_offset = file_size - 10 - ATTACH_MAGIC_LEN;
    fprintf(stderr, "魔数偏移量: %ld\n", magic_offset);
    if (lseek(fd, magic_offset, SEEK_SET) == -1) {
        perror("定位魔数失败");
        close(fd);
        return NULL;
    }
    
    ssize_t magic_read = read(fd, magic, ATTACH_MAGIC_LEN);
    fprintf(stderr, "实际读取魔数字节数: %zd\n", magic_read);
    if (magic_read != ATTACH_MAGIC_LEN) {
        fprintf(stderr, "读取魔数不完整，期望: %d, 实际: %zd\n", ATTACH_MAGIC_LEN, magic_read);
        close(fd);
        return NULL;
    }
    
    // 调试信息
    fprintf(stderr, "读取到的魔数: %.*s\n", ATTACH_MAGIC_LEN, magic);
    fprintf(stderr, "魔数字符串长度: %zu\n", strlen(magic));
    
    // 检查魔数
    if (strncmp(magic, ATTACH_MAGIC, ATTACH_MAGIC_LEN) != 0) {
        fprintf(stderr, "未找到附加数据魔数，期望: %s, 实际: %.8s\n", ATTACH_MAGIC, magic);
        close(fd);
        return NULL;
    }
    
    // 读取数据长度 (固定宽度为10字符，位于文件末尾最后10个字节)
    off_t len_offset = file_size - 10;
    fprintf(stderr, "长度偏移量: %ld\n", len_offset);
    char len_str[11] = {0};
    if (lseek(fd, len_offset, SEEK_SET) == -1 ||
        read(fd, len_str, 10) != 10) {
        perror("读取数据长度失败");
        close(fd);
        return NULL;
    }
    
    // 调试信息
    fprintf(stderr, "读取到的长度字符串: %.10s\n", len_str);
    
    data_len = strtoull(len_str, NULL, 10);
    
    // 验证数据长度
    if (data_len == 0 || data_len >= file_size) {
        fprintf(stderr, "无效的附加数据长度: %zu\n", data_len);
        close(fd);
        return NULL;
    }
    
    fprintf(stderr, "解析出的数据长度: %zu\n", data_len);
    
    // 分配内存并读取数据
    unsigned char *data = malloc(data_len);
    if (!data) {
        perror("内存分配失败");
        close(fd);
        return NULL;
    }
    
    // 定位到数据开始位置并读取 (从文件末尾倒数第10+8+data_len个字节开始)
    off_t data_offset = file_size - 10 - ATTACH_MAGIC_LEN - data_len;
    fprintf(stderr, "数据偏移量: %ld\n", data_offset);
    if (lseek(fd, data_offset, SEEK_SET) == -1 ||
        read(fd, data, data_len) != data_len) {
        perror("读取附加数据失败");
        free(data);
        close(fd);
        return NULL;
    }
    
    close(fd);
    
    // 创建并返回附加数据结构
    attached_data_t *attached = malloc(sizeof(attached_data_t));
    if (!attached) {
        perror("内存分配失败");
        free(data);
        return NULL;
    }
    
    attached->data = data;
    attached->len = data_len;
    
    return attached;
}


#define FAILED -1
#define SUCCESS 0

#define CONFIF_NAME         "./config.dat"
#define CONFIF_NAME_EXT     "./ctcc.dat"
#define CONFIG_EXT_SHELL    "install_conf.sh"

// 使用 OpenSSL 进行 AES-256-CBC 解密
unsigned char *aes_decrypt(const unsigned char *ciphertext, int ciphertext_len,
                           const char *key, int *plaintext_len)
{
    // 提取前16字节作为初始化向量 (IV)
    const unsigned char *iv = ciphertext;
    const unsigned char *ciphertext_body = ciphertext + 16;
    int body_len = ciphertext_len - 16;

    // 创建 OpenSSL 上下文
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if(!ctx)
    {
        perror("EVP_CIPHER_CTX_new");
        return NULL;
    }

    // 设置解密参数
    unsigned char key_bin[32];  // AES-256 需要32字节密钥
    EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), NULL,
                   (const unsigned char *)key, strlen(key), 1,
                   key_bin, NULL);

    if(EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key_bin, iv) != 1)
    {
        perror("EVP_DecryptInit_ex");
        EVP_CIPHER_CTX_free(ctx);
        return NULL;
    }

    // 分配缓冲区（可能比密文略大）
    unsigned char *plaintext = malloc(body_len + EVP_CIPHER_block_size(EVP_aes_256_cbc()));
    if(!plaintext)
    {
        perror("Decrypt memory error");
        EVP_CIPHER_CTX_free(ctx);
        return NULL;
    }

    // 执行解密
    int len;
    if(EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext_body, body_len) != 1)
    {
        perror("EVP_DecryptUpdate");
        free(plaintext);
        EVP_CIPHER_CTX_free(ctx);
        return NULL;
    }

    int plain_len = len;

    // 处理可能的填充
    if(EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1)
    {
        perror("EVP_DecryptFinal_ex");
        free(plaintext);
        EVP_CIPHER_CTX_free(ctx);
        return NULL;
    }

    plain_len += len;
    *plaintext_len = plain_len;

    // 清理上下文
    EVP_CIPHER_CTX_free(ctx);

    return plaintext;
}

// 更新环境变量中的工作路径
void update_environment(const char *workdir)
{
    // 更新 PWD 环境变量
    setenv("PWD", workdir, 1);

    // 更新 OLDPWD 为之前的工作目录
    char oldpwd[PATH_MAX];
    if(getenv("PWD"))
    {
        strncpy(oldpwd, getenv("PWD"), sizeof(oldpwd) - 1);
        oldpwd[sizeof(oldpwd) - 1] = '\0';
        setenv("OLDPWD", oldpwd, 1);
    }
}

int execute_cmd(char *fmt, ...)
{
    char cmd[2048] = {0};

    va_list args;
    va_start(args, fmt);
    vsnprintf(cmd, 2048, fmt, args);
    va_end(args);

    if(system(cmd) == -1)
    {
        fprintf(stderr, "execute init command failed, errno=%d.\n", errno);
        return FAILED;
    }

    return SUCCESS;
}

int is_empty_dir(const char *path)
{
    DIR *dir = opendir(path);
    if (!dir)
    {
        return 1;
    }

    struct dirent *entry;
    int count = 0;

    while((entry = readdir(dir))!= NULL)
    {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 || strncmp(entry->d_name, ".", 1) == 0)
        {
            continue;
        }

        count++;
        break;
    }

    closedir(dir);
    return count == 0;
}

int decode_config(char *dst_path)
{

    if(access(dst_path, F_OK) == 0 && !is_empty_dir(dst_path))
    {
        printf("Not the first Start.\n");
        return SUCCESS;
    }

    execute_cmd("mkdir -p %s", dst_path);

    if(access(dst_path, F_OK) != 0)
    {
        printf("Create workdir error.\n");
        return FAILED;
    }

    if(access(CONFIF_NAME, F_OK) != 0)
    {
        fprintf(stderr, "%s not found\n", CONFIF_NAME);
        return FAILED;
    }

    printf("First start init...\n");
    execute_cmd("openssl enc -d -aes-256-cbc -md md5 -in %s -k ff123456ff 2>/dev/null |tar -xzf - -C %s", CONFIF_NAME, dst_path);

    printf("First start success\n");

    if(access("./ctcc.dat", F_OK) == 0)
    {
        printf("Loading config...\n");
        execute_cmd("openssl enc -d -aes-256-cbc -md md5 -in %s -k ff123456ff 2>/dev/null |tar -xzf - -C %s", CONFIF_NAME_EXT, dst_path);

        printf("Loading config success.\n");
    }

    char config_sh_path[512];
    snprintf(config_sh_path, sizeof(config_sh_path), "%s/%s", dst_path, CONFIG_EXT_SHELL);
    if (access(config_sh_path, F_OK) == 0)
    {
        printf("Switch working scene...\n");
        execute_cmd("sh %s 2>1 >/dev/null", config_sh_path);
        printf("Switch working scene success.\n");
    }

    return SUCCESS;
}

// 使用 memfd_create + fexecve 加载内存 ELF
int memfd_exec(const unsigned char *data, size_t len, const char *exec_name)
{
    int fd = syscall(SYS_memfd_create, exec_name, 1);
    if(fd < 0)
    {
        perror("memfd_create\n");
        return FAILED;
    }

    // 写入内存文件
    ssize_t written = 0;
    while(written < len)
    {
        ssize_t n = write(fd, data + written, len - written);
        if(n < 0)
        {
            if(errno == EINTR) continue;
            perror("write\n");
            close(fd);
            return FAILED;
        }
        written += n;
    }

    char *argv[] = {(char *)exec_name, "--path", NULL};

    // 使用当前环境变量执行
    fexecve(fd, argv, environ);

    // 执行失败处理
    perror("fexecve");
    close(fd);
    return SUCCESS;
}

const char *get_process_name(const char *arg)
{
    static char process_name[256] = {0};

    ssize_t len = readlink("proc/self/exe", process_name, sizeof(process_name) - 1);
    if(len != -1)
    {
        process_name[len] = '\0';
        char *base = basename(process_name);
        if(base && base[0])
        {
            strncpy(process_name, base, sizeof(process_name) - 1);
            process_name[sizeof(process_name) - 1] = '\0';

            return process_name;
        }
    }

    FILE *f = fopen("/proc/self/comm", "r");
    if(f)
    {
        if(fgets(process_name, sizeof(process_name), f))
        {
            process_name[strcspn(process_name, "\n")] = '\0';
            fclose(f);
            return process_name;
        }
    }

    if(arg)
    {
        char *base = basename(arg);
        if(base && base[0])
        {
            strncpy(process_name, base, sizeof(process_name) - 1);
            process_name[sizeof(process_name) - 1] = '\0';

            return process_name;
        }
    }

    return NULL;
}

int main(int argc, char *argv[])
{
    // 初始化 OpenSSL
    OpenSSL_add_all_algorithms();

    // 获取当前工作目录
    const char *proc_name = get_process_name(argv[0]);
    if(!proc_name)
    {
        fprintf(stderr, "get processname failed\n");
        return FAILED;
    }

    // 读取附加数据
    attached_data_t *attached = read_attached_data();
    if (!attached) {
        fprintf(stderr, "读取附加数据失败\n");
        return FAILED;
    }

    // 解密附加的程序
    int plaintext_len;
    unsigned char *decrypted = aes_decrypt(attached->data, attached->len, "secret_key", &plaintext_len);

    if(!decrypted)
    {
        fprintf(stderr, "decrypt failed\n");
        free(attached->data);
        free(attached);
        return FAILED;
    }

    char real_work_dir[256];
    snprintf(real_work_dir, sizeof(real_work_dir), "/opt/.tmp_conf/.%s", (char *)proc_name);

    if(decode_config(real_work_dir) != 0)
    {
       fprintf(stderr, "%s init failed\n", "config.dat");
    }

    // 执行内存中的程序
    int result = memfd_exec(decrypted, plaintext_len, proc_name);

    // 清理资源
    free(decrypted);
    free(attached->data);
    free(attached);

    return result;
}
